package com.example.demo.utils;

import com.example.demo.bean.SysJob;
import com.example.demo.bean.SysJobLog;
import com.example.demo.constant.ScheduleConstants;
import com.example.demo.service.ISysJobLogService;
import com.example.demo.util.SpringUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * 抽象quartz调用
 *
 * @author ruoyi
 */
public abstract class AbstractQuartzJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);

    /**
     * 线程本地变量
     */
    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        SysJob sysJob = new SysJob();
        try {
            Object jobData = context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES);
            if (jobData == null) {
                log.error("任务数据为空，无法执行任务");
                throw new JobExecutionException("任务数据为空");
            }
            BeanUtils.copyProperties(jobData, sysJob);
            
            if (sysJob == null || sysJob.getJobId() == null) {
                log.error("任务信息不完整，无法执行任务");
                throw new JobExecutionException("任务信息不完整");
            }
            
            log.info("开始执行任务：{}", sysJob.getJobName());
            before(context, sysJob);
            doExecute(context, sysJob);
            after(context, sysJob, null);
            log.info("任务执行完成：{}", sysJob.getJobName());
        } catch (Exception e) {
            log.error("任务执行异常：{}", e.getMessage(), e);
            after(context, sysJob, e);
            throw new JobExecutionException(e);
        }
    }

    /**
     * 执行前
     *
     * @param context 工作执行上下文对象
     * @param sysJob  系统计划任务
     */
    protected void before(JobExecutionContext context, SysJob sysJob) {
        threadLocal.set(new Date());
        log.info("任务开始执行：{}，开始时间：{}", sysJob.getJobName(), threadLocal.get());
    }

    /**
     * 执行后
     *
     * @param context 工作执行上下文对象
     * @param sysJob  系统计划任务
     */
    protected void after(JobExecutionContext context, SysJob sysJob, Exception e) {
        Date startTime = threadLocal.get();
        threadLocal.remove();

        if (startTime == null) {
            log.warn("任务开始时间为空：{}", sysJob.getJobName());
            return;
        }

        final SysJobLog sysJobLog = new SysJobLog();
        sysJobLog.setJobName(sysJob.getJobName());
        sysJobLog.setJobGroup(sysJob.getJobGroup());
        sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
        sysJobLog.setStartTime(startTime);
        sysJobLog.setStopTime(new Date());
        sysJobLog.setCreateTime(sysJobLog.getStopTime());
        long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
        sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时：" + runMs + "毫秒");
        
        if (e != null) {
            sysJobLog.setStatus(0);
            String errorMsg = StringUtils.substring(e.getMessage(), 0, 2000);
            sysJobLog.setExceptionInfo(errorMsg);
            log.error("任务执行失败：{}，异常信息：{}", sysJobLog.getJobName(), errorMsg);
        } else {
            sysJobLog.setStatus(1);
            log.info("任务执行成功：{}，总共耗时：{}毫秒", sysJobLog.getJobName(), runMs);
        }

        // 异步保存任务日志，避免事务问题
        try {
            // 使用异步方式保存日志，避免阻塞任务执行
            new Thread(() -> {
                try {
                    SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
                } catch (Exception ex) {
                    log.error("异步保存任务日志失败：{}", ex.getMessage(), ex);
                }
            }).start();
        } catch (Exception ex) {
            log.error("启动异步保存任务日志失败：{}", ex.getMessage(), ex);
        }
    }

    /**
     * 执行方法，由子类重载
     *
     * @param context 工作执行上下文对象
     * @param sysJob  系统计划任务
     * @throws Exception 执行过程中的异常
     */
    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
